/***************************************************************************
 *   Copyright (C) 2007 by saa   *
 *   root@saa   *
 ***************************************************************************/

#include <QKeySequence>
#include <QVariant>
#include <QHostInfo>

#include "telnetclient.h"
#include "tracerouteresultparser.h"
#include "compilemode.h"

TelnetClient::TelnetClient(QObject * parent) :
    QtTelnet(parent),
    tracerouteResult(""),
    tracerouteState(false),
    emptyStringCount(0),
    stringAsteriskCount(0),
    m_attemptLimit(ATTEMPT_LIMIT)
{
    connect(this, SIGNAL(connected()),
            this, SLOT (tcpConnected()));
    connect(this, SIGNAL(message(const QString &)),
            this, SLOT(telnetMessage(const QString &)));
    connect(this, SIGNAL(loginRequired()),
            this, SLOT(telnetLoginRequired()));
    connect(this, SIGNAL(loginFailed()),
            this, SLOT(telnetLoginFailed()));
    connect(this, SIGNAL(loggedOut()),
            this, SLOT(telnetLoggedOut()));
    connect(this, SIGNAL(loggedIn()),
            this, SLOT(telnetLoggedIn()));
    connect(this, SIGNAL(connectionError(QAbstractSocket::SocketError)),
            this, SLOT(telnetConnectionError(QAbstractSocket::SocketError)));
}

void TelnetClient::telnetMessage(const QString &msg)
{
#ifdef DEBUG
#if 0
    log(stripCR(msg));
#endif
#endif
    if(tracerouteState){
    /* try to interrupt traceroute after SILENT_LIMIT dont response host */
        if(msg.indexOf("*") != -1 || msg.indexOf("?") != -1){
            if(++stringAsteriskCount >= 3){
                stringAsteriskCount = 0;
                if(++emptyStringCount >= SILENT_LIMIT){
                    sendEscapeSequence();
                }
            }
        } else {
            stringAsteriskCount = 0;
            tracerouteResult += stripCR(msg);
        }
    } else if (msg.endsWith(m_routeServer.promptPattern + ">")){
        nextTarget();
    }
}

void TelnetClient::telnetLoginRequired()
{
    log("login required");
    login(m_routeServer.username, m_routeServer.password);
}

void TelnetClient::telnetLoginFailed()
{
    log("Login failed");
}

void TelnetClient::telnetLoggedOut()
{
    log("Logged out");
}

void TelnetClient::telnetLoggedIn()
{
    if(!tracerouteResult.isEmpty() && tracerouteState){
        /* traceroute finished, parse result */
        log(QString("traceroute to %1 finished").arg(m_currentTarget));
        emit tracerouteToTargetFinished(m_routeServer.hostname, m_currentTarget);
        tracerouteState = false;
        
        TracerouteResultParser * parser(new TracerouteResultParser(this));
        connect(parser, SIGNAL(finished()), parser, SLOT(deleteLater()));
        connect(this, SIGNAL(parseString(const QString&)), \
                parser, SLOT(parseTracerouteResult(const QString &)));
        connect(parser, SIGNAL(parsingFinished(QList<tracerouteNode>)), \
                this, SLOT(nodeListTolinkList(QList<tracerouteNode>)));
        connect(parser, SIGNAL(logParserMessage(const QString &, const LogFlag::LogLevels)), \
                this, SLOT(log(const QString &, const LogFlag::LogLevels)));

#ifdef ONE_THREAD
        parser->parseTracerouteResult(tracerouteResult);
        delete parser;
#else
        emit parseString(tracerouteResult);
#endif
    }
    //nextTarget();
}

void TelnetClient::telnetConnectionError(QAbstractSocket::SocketError error)
{
    log(QString(tr("Connection error: %1")).arg(error));
    if(--m_attemptLimit){
        log(QString(tr("Remainder probe number : %1")).arg(m_attemptLimit));
        run();
    } else {
        log(tr("Cannot connect to host"));
        emit ready(this);
    }
}

void TelnetClient::suspend()
{
    sendControl(QtTelnet::Suspend);
}

void TelnetClient::tcpConnected()
{
}
    
QString TelnetClient::stripCR(const QString &msg)
{
    QString nmsg(msg);
    return nmsg.replace('\r', "");
}

void TelnetClient::run( )
{
#ifdef DEBUG
    log("hostname:        " + m_routeServer.hostname, LogFlag::Parser_DEBUG);
    log("username:        " + m_routeServer.username, LogFlag::Parser_DEBUG);
    log("password:        " + m_routeServer.password, LogFlag::Parser_DEBUG);
    log("passwordPattern: " + m_routeServer.passwordPattern, LogFlag::Parser_DEBUG);
    log("loginPattern:    " + m_routeServer.loginPattern, LogFlag::Parser_DEBUG);
    log("promptPattern:   " + m_routeServer.promptPattern, LogFlag::Parser_DEBUG);
#endif

    QHostInfo startHostInfo(QHostInfo::fromName(m_routeServer.hostname));
    if (!startHostInfo.addresses().isEmpty())
        serverIP = startHostInfo.addresses().first().toString();

    if(!m_routeServer.loginPattern.isEmpty())
        setLoginPattern(QRegExp(m_routeServer.loginPattern, Qt::CaseInsensitive, QRegExp::Wildcard));
    if(!m_routeServer.passwordPattern.isEmpty())
        setPasswordPattern(QRegExp(m_routeServer.passwordPattern, Qt::CaseInsensitive, QRegExp::Wildcard));
    if(!m_routeServer.promptPattern.isEmpty())
        setPromptPattern(QRegExp(m_routeServer.promptPattern + ">", Qt::CaseInsensitive, QRegExp::Wildcard));

    connectToHost(m_routeServer.hostname);
}


void TelnetClient::nextTarget( )
{
    if(!m_targetList.isEmpty()){
        tracerouteResult.clear();
        m_currentTarget = m_targetList.takeFirst();
        if(m_currentTarget.isEmpty()){
            log(QString("Empty traceroute target! Try next one"));
            nextTarget();
            return;
        }
        log(QString("Traceroute to %1 started").arg(m_currentTarget));
        emit tracerouteToTargetStarted(m_routeServer.hostname, m_currentTarget);
        sendData("traceroute " + m_currentTarget);
        tracerouteState = true;
        emptyStringCount = 0;
        stringAsteriskCount = 0;
    } else {
        sendData("logout");
        log(QString("All target done, logout from, %1").arg(m_routeServer.hostname));
        disconnectFromHost();
        emit ready(this);
#ifndef ONE_THREAD
        thread()->quit();
#endif    
    }
}

void TelnetClient::nodeListTolinkList(QList<tracerouteNode> tracerouteNodeList)
{
    LinkList linkList;
    
    if(!tracerouteNodeList.isEmpty()){

        // use the first IP address
        for(int i = 0; i < tracerouteNodeList.first().hostIP.size(); ++i){
            if(!tracerouteNodeList.first().hostIP.at(i).isEmpty() && tracerouteNodeList.first().ttl.at(i) == 1)
                linkList.append(Link(QHostAddress(serverIP).toIPv4Address(),
                                 QHostAddress(tracerouteNodeList.first().hostIP.at(i)).toIPv4Address(),
                                 tracerouteNodeList.first().tripTime.at(i).toDouble()));
        }

        for (int i = 1; i < tracerouteNodeList.size(); ++i) 
            for(int j = 0; j < tracerouteNodeList.at(i).hostIP.size() && j < tracerouteNodeList.at(i-1).hostIP.size(); ++j){
            Q_ASSERT(tracerouteNodeList.at(i).tripTime.size() > j);
                if(!tracerouteNodeList.at(i - 1).hostIP.at(j).isEmpty() &&
                   !tracerouteNodeList.at(i).hostIP.at(j).isEmpty() &&
                    tracerouteNodeList.at(i).ttl.at(j) - 1 == tracerouteNodeList.at(i-1).ttl.at(j))
                    linkList.append(Link(QHostAddress(tracerouteNodeList.at(i - 1).hostIP.at(j)).toIPv4Address(),
                                        QHostAddress(tracerouteNodeList.at(i).hostIP.at(j)).toIPv4Address(),
                                        tracerouteNodeList.at(i).tripTime.at(j).toDouble()));
            }
    }

#ifdef DEBUG
    for (int i = 0; i < linkList.size(); ++i)
        log(QString("%1 %2 %3").\
                arg(QHostAddress(linkList.at(i).begin).toString()).\
                arg(QHostAddress(linkList.at(i).end).toString()).\
                arg(linkList.at(i).averageTime),\
                LogFlag::TelnetClient_DEBUG);
#endif

    emit saveResults(linkList);
}

void TelnetClient::log(const QString & message, const LogFlag::LogLevels logLevel) const
{
    emit logTelnetMessage(m_routeServer.hostname + ": " + message, logLevel);
}

